package com.cawring.simple.route;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import org.apache.commons.lang3.StringUtils;

import com.google.common.base.Charsets;
import com.google.common.io.Files;

/**
 * 
 * <pre>
 * 클래스명 : ROUTE 제어
 * 패키지명 : com.cawring.simple.route
 * 파일명   : Routes.java
 * 설명     :
 * routes 파일의 유효성 체크 및 url, controller 분기
 * </pre>
 * @Author    : 백승건
 * @Date      : 2014. 3. 8.
 * @Version   : v.1.0.0 - 2014. 3. 8. 최초작성
 */
public class Routes {

	/* 로그 */
	private static final Logger log = Logger.getLogger(Routes.class.getName());
	
	/* singleton */
	private static Routes routes = new Routes();
	
	/* POST method route info */
	private static Map<String, RouteVO> methodPostMap;
	
	/* GET method route info */
	private static Map<String, RouteVO> methodGetMap;
	
	/**
	 * <pre>
	 * 메소드명     : 싱글턴 인스턴스 메소드
	 * 메소드작성일 : 2014. 3. 8.
	 * 메소드설명   : 싱글턴 패턴을 적용
	 * </pre>
	 */
	public static Routes getInstance()
	{
		return routes;
	}

	/**
	 * <pre>
	 * 메소드명     : 생성자 메소드
	 * 메소드작성일 : 2014. 3. 8.
	 * 메소드설명   : private
	 * </pre>
	 */
	private Routes()
	{
		/* routes files */
		File[]         routes = null;
		
		/* post method map */
		methodPostMap = new LinkedHashMap<String, RouteVO>();
		
		/* get method map */
		methodGetMap  = new LinkedHashMap<String, RouteVO>();
		
		try
		{
			/* routes 파일 디렉토리 */
			URL url = Routes.class.getProtectionDomain().getCodeSource().getLocation();
			
			/* 디렉토리에서 파일을 찾는다. */
			File           targetDirectory = new File(url.toURI());     // routes 파일이 있어야 하는 경로
			FilenameFilter filter          = new RouteFileNameFilter(); // routes 파일 허용 체크 필터
			
			/* routes 파일 */
			routes = targetDirectory.listFiles(filter); // routes 파일목록
			
			/* [ERROR] routes 파일이 없다 */
			if(routes == null || routes.length == 0) throw new NullPointerException();
			
			/* routes 파일 parse */
			for(File route : routes)
			{
				/* 파일을 읽어 1line씩 list에 담는다. */
				List<String> lines = Files.readLines(route, Charsets.UTF_8);
				
				/* line list loop */
				for(String line : lines)
				{
					/* 정보가 없는 경우 continue */
					if(RouteUtils.isLineSkip(line)) continue;
					
					/* 라우트 정보를 vo에 담는다 */
					RouteVO vo = RouteUtils.getRouteInfo(line);
					
					/* route add */
					add(vo);
				}
			}
		}
		catch (URISyntaxException e)
		{
			log.severe("routes file path not found!!!!!");
		}
		catch (NullPointerException e)
		{
			log.severe("routes file not found!!!!!");
		}
		catch (IOException e)
		{
			log.severe("routes file error!!!!!");
		}
	}
	
	/**
	 * 
	 * <pre>
	 * 메소드명     : 라우터 정보 추가
	 * 메소드작성일 : 2014. 3. 11.
	 * 메소드설명   :
	 * 라우터 정보를 추가한다. 정보를 추가 시, 중복 시 에러를 발생한다
	 * </pre>
	 * @param vo : 라우터 정보
	 */
	public void add(RouteVO vo)
	{
		/* vo가 null 일 경우는 false */
		if(vo == null) return;
		
		/* isNotEmpty - 값이 비어 있을 경우 false 반환 */
		if(!vo.isNotEmpty()) return;
		
		/* 변수 */
		String path   = vo.getUrl();       // path 값은 각 method값에 대한 map의 키값으로 쓰인다.
		String method = vo.getMethod();     // url method 값 
		
		/* method 값이 GET인 경우 getMap을 바라본다. */
		if(StringUtils.equals(RouteConstants.METHOD_GET, method))
		{
			/* 값이 중복됐으면, 키값 중복 에러 발생 */
			if(methodGetMap.containsKey(path))
			{
				throw new RouteParseException(RouteConstants.METHOD_GET+ "메소드에 path값인 "+path+" 가 중복되었습니다.");
			}
			
			/* map에 값을 추가한다. */
			methodGetMap.put(path, vo);
		}
		/* method 값이 POST인 경우 getMap을 바라본다. */
		else if(StringUtils.equals(RouteConstants.METHOD_POST, method))
		{
			/* 값이 중복됐으면, 키값 중복 에러 발생 */
			if(methodPostMap.containsKey(path))
			{
				throw new RouteParseException(RouteConstants.METHOD_POST+ "메소드에 path값인 "+path+" 가 중복되었습니다.");
			}
			
			/* map에 값을 추가한다. */
			methodPostMap.put(path, vo);
		}
	}
	
	/**
	 * 
	 * <pre>
	 * 메소드명     : 라우터 정보 불러오기
	 * 메소드작성일 : 2014. 3. 12.
	 * 메소드설명   :
	 * 라우터 정보를 불러온다.
	 * </pre>
	 * @param method : url method값
	 * @param url    : url
	 * @return 라우터 정보
	 */
	public RouteVO get(String method, String url)
	{
		/* METHOD가 GET인 경우 url(path)을 키값으로 methodGetMap을 바라본다 */
		if(StringUtils.equals(RouteConstants.METHOD_GET, method))
		{
			return methodGetMap.get(url);
		}
		/* METHOD가 POST인 경우 url(path)을 키값으로 methodPostMap을 바라본다 */
		else if(StringUtils.equals(RouteConstants.METHOD_POST, method))
		{
			return methodPostMap.get(url);
		}
		
		/* 메소드 값이 일치하지 않으면 null을 던진다 */
		return null;
	}
	
}
